package com.fxbin123.handler;

import com.fxbin123.domain.City;
import com.fxbin123.repository.CityRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * created with IntelliJ IDEA.
 * author: fxbin
 * date: 2018/7/15
 * time: 16:12
 * description:
 */

/**
 * @Component 泛指组件，当组件不好归类的时候，使用该注解进行标注
 */
@Slf4j
@Component
public class CityHandler {

    /**
     * final 和 @Autowired 标注在构造器注入 CityRepository Bean
     *
     *
     * 从返回值可以看出，Mono 和 Flux 适用于两个场景，即：
     *
     * Mono：实现发布者，并返回 0 或 1 个元素，即单对象。
     * Flux：实现发布者，并返回 N 个元素，即 List 列表对象
     *
     * 为啥不直接返回对象???
     *      直接使用 Flux 和 Mono 是非阻塞写法，相当于回调方式
     *      利用函数式可以减少了回调，因此会看不到相关接口
     *      这恰恰是 WebFlux 的好处：集合了非阻塞 + 异步
     *
     *
     * Mono 常用的方法有：
     *
     *  Mono.create()：使用 MonoSink 来创建 Mono。
     *  Mono.justOrEmpty()：从一个 Optional 对象或 null 对象中创建 Mono。
     *  Mono.error()：创建一个只包含错误消息的 Mono。
     *  Mono.never()：创建一个不包含任何消息通知的 Mono。
     *  Mono.delay()：在指定的延迟时间之后，创建一个 Mono，产生数字 0 作为唯一值。
     */

    private final CityRepository cityRepository;

    @Autowired
    public CityHandler(CityRepository cityRepository) {
        this.cityRepository = cityRepository;
    }

    @Autowired
    private RedisTemplate redisTemplate;

    public Mono<City> save(City city) {
        return cityRepository.save(city);
    }

    public Mono<City> findCityById(Long id){
        // 从缓存中获取城市信息
        String key = "city_" + id;
        ValueOperations<String, City> operations = redisTemplate.opsForValue();

        // 缓存存在
        Boolean hasKey = redisTemplate.hasKey(key);
        if(hasKey){
            City city = operations.get(key);
            log.info(" CityHandler.findCityById(); 从缓存中获取了城市 >> {}", city);
            return Mono.create(cityMonoSink -> cityMonoSink.success(city));
        }

        // 从Mongo 中获取城市信息
        Mono<City> cityMono = cityRepository.findById(id);
        if(cityMono == null)
            return cityMono;

        cityMono.subscribe(cityObj -> {
           operations.set(key, cityObj);
        });


        return cityMono;
    }

    public Flux<City> findAllCity(){
        return cityRepository.findAll().cache();
    }

    public Mono<City> modifyCity(City city) {
        Mono<City> cityMono = cityRepository.save(city);

        // 缓存存在，删除缓存
        String key = "city_" + city.getId();
        boolean hasKey = redisTemplate.hasKey(key);
        if (hasKey) {
            redisTemplate.delete(key);

            log.info("CityHandler.modifyCity() : 从缓存中删除城市 ID >> {} ", city.getId());
        }

        return cityMono;
    }

    public Mono<Long> deleteCity(Long id) {
        cityRepository.deleteById(id);

        // 缓存存在，删除缓存
        String key = "city_" + id;
        boolean hasKey = redisTemplate.hasKey(key);
        if (hasKey) {
            redisTemplate.delete(key);

            log.info("CityHandler.deleteCity() : 从缓存中删除城市 ID >> {} ", id);
        }

        return Mono.create(cityMonoSink -> cityMonoSink.success(id));
    }

    public Mono<City> getByCityName(String cityName){
        return cityRepository.findByCityName(cityName);
    }

}
